﻿using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Drawing;

namespace SOFM {
	public delegate void EndEpochEventHandler(object sender, EndEpochEventArgs e);
	public delegate void EndIterationEventHandler(object sender, EventArgs e);

	public class NeuralNetwork {
		private Neuron[,] outputLayer;

		/// <summary>
		/// Không gian 2 chiều
		/// </summary>
		public Neuron[,] OutputLayer {
			get { return outputLayer; }
			set { outputLayer = value; }
		}
		/// <summary>
		/// Độ dài Vector đầu vào
		/// </summary>
		private int inputLayerDimension;

		/// <summary>
		/// Số Nơ ron đầu ra (Ma trận đầu ra)
		/// </summary>
		private int outputLayerDimension;

		/// <summary>
		/// Số Vector đầu vào
		/// </summary>
		private int numberOfPatterns;

		/// <summary>
		/// Danh sách các Vector
		/// </summary>
		private List<List<double>> patterns;

		/// <summary>
		/// Danh sách tên các cụm: A,B,A,A,C
		/// </summary>
		private List<string> classes;

		/// <summary>
		/// Danh sách tên các cụm đã được gộp lại và sắp xếp: A, B, C
		/// </summary>
		private SortedList<string, int> existentClasses;
		private List<System.Drawing.Color> usedColors;
		private bool normalize;
		private int maxIterationNumber;
		private int currentIteration;

		private Functions activationFunction;
		private double minEpsilon;
		private double currentEpsilon;

		/// <summary>
		/// Tính khoảng cách 2 vector
		/// </summary>
		/// <param name="vector1"></param>
		/// <param name="vector2"></param>
		/// <returns></returns>
		private double CalculateNormOfVectors(List<double> vector1, List<double> vector2) {
			double value = 0;
			for (int i = 0; i < vector1.Count; i++)
				value += Math.Pow((vector1[i] - vector2[i]), 2);
			value = Math.Sqrt(value);
			return value;
		}

		/// <summary>
		/// Chuẩn hóa đầu vào xi = xi/(trung bình nhân của các xi)
		/// </summary>
		/// <param name="pattern"></param>
		private void NormalizeInputPattern(List<double> pattern) {
			double nn = 0;
			for (int i = 0; i < inputLayerDimension; i++) {
				nn += (pattern[i] * pattern[i]);
			}
			nn = Math.Sqrt(nn);
			for (int i = 0; i < inputLayerDimension; i++) {
				pattern[i] /= nn;
			}
		}

		private void StartEpoch(List<double> pattern) {
			Neuron Winner = this.FindWinner(pattern);
			currentEpsilon = 0;
			for (int i = 0; i < outputLayerDimension; i++)
				for (int j = 0; j < outputLayerDimension; j++) {
					currentEpsilon += outputLayer[i, j].ModifyWeights(pattern, Winner.Coordinate, currentIteration, activationFunction);
				}
			currentIteration++;

			//currentEpsilon = trọng số trung bình của các vector cập nhật, nếu ít thay đổi -> currentEpsilon đủ nhỏ -> thoát
			currentEpsilon = Math.Abs(currentEpsilon / (outputLayerDimension * outputLayerDimension));

			EndEpochEventArgs e = new EndEpochEventArgs();
			OnEndEpochEvent(e);
		}

		public bool Normalize {
			get { return normalize; }
			set { normalize = value; }
		}

		public List<List<double>> Patterns {
			get { return patterns; }
		}

		public List<string> Classes {
			get { return classes; }
		}

		public int InputLayerDimension {
			get { return inputLayerDimension; }
		}

		public int OutputLayerDimension {
			get { return outputLayerDimension; }
		}

		public double CurrentDelta {
			get { return currentEpsilon; }
		}

		public SortedList<string, int> ExistentClasses {
			get { return existentClasses; }
		}

		public List<System.Drawing.Color> UsedColors {
			get { return usedColors; }
		}

		private int NumberOfClasses() {
			existentClasses = new SortedList<string, int>();
			existentClasses.Add(classes[0], 1);
			int k = 0;
			int d = 2;
			for (int i = 1; i < classes.Count; i++) {
				k = 0;
				for (int j = 0; j < existentClasses.Count; j++)
					if (existentClasses.IndexOfKey(classes[i]) != -1) k++;
				if (k == 0) {
					existentClasses.Add(classes[i], d);
					d++;
				}
			}
			return existentClasses.Count;
		}

		/// <summary>
		/// Tạo ra ma trận màu cho các nơ-ron đầu ra
		/// </summary>
		/// <returns></returns>
		public System.Drawing.Color[,] ColorSOFM() {
			System.Drawing.Color[,] colorMatrix = new System.Drawing.Color[outputLayerDimension, outputLayerDimension];
			int numOfClasses = NumberOfClasses();
			List<System.Drawing.Color> goodColors = new List<System.Drawing.Color>();
			goodColors.Add(System.Drawing.Color.Black);
			goodColors.Add(System.Drawing.Color.Red);
			goodColors.Add(System.Drawing.Color.Navy);
			goodColors.Add(System.Drawing.Color.Green);
			goodColors.Add(System.Drawing.Color.Yellow);
			usedColors = new List<System.Drawing.Color>(numOfClasses);
			usedColors.Add(goodColors[0]);
			int k = 0;
			int randomColor = 0;
			Random r = new Random();
			while (usedColors.Count != numOfClasses) {
				k = 0;
				randomColor = r.Next(goodColors.Count);
				foreach (System.Drawing.Color cl in usedColors)
					if (cl == goodColors[randomColor])
						k++;

				if (k == 0)
					usedColors.Add(goodColors[randomColor]);
			}

			// Khởi tạo ban đầu nơ-ron nào cũng nhận màu ButtonFace - Unknown
			for (int i = 0; i < outputLayerDimension; i++)
				for (int j = 0; j < outputLayerDimension; j++)
					colorMatrix[i, j] = System.Drawing.Color.FromKnownColor(System.Drawing.KnownColor.ButtonFace);

			// Chỉ hiển thị màu của các winner nơ-ron
			// đối với danh sách cấu mẫu đầu vào
			for (int i = 0; i < patterns.Count; i++) {
				Neuron winnerNeuron = FindWinner(patterns[i]);
				colorMatrix[winnerNeuron.Coordinate.X, winnerNeuron.Coordinate.Y] = usedColors[existentClasses[classes[i]] - 1];
			}

			return colorMatrix;
		}

		/// <summary>
		/// Khởi tạo mạng nơ ron
		/// </summary>
		/// <param name="m">Số nơ ron đầu ra</param>
		/// <param name="numberOfIterations">Số bước tối đa</param>
		/// <param name="minEpsilon">trọng số học mạng</param>
		/// <param name="activationFunction">Luật học</param>
		public NeuralNetwork(int m, int numberOfIterations, double minEpsilon, Functions activationFunction) {
			this.outputLayerDimension = m;
			this.currentIteration = 1;
			this.maxIterationNumber = numberOfIterations;
			this.activationFunction = activationFunction;
			this.minEpsilon = minEpsilon;

			// Tốc độ học hiện tại
			this.currentEpsilon = 100;
		}

		/// <summary>
		/// Từ Partern tìm nơ ron đầu ra (2D) gần nhất - Winner
		/// </summary>
		/// <param name="pattern"></param>
		/// <returns></returns>
		public Neuron FindWinner(List<double> pattern) {
			//List<double> norms = new List<double>(outputLayerDimension * outputLayerDimension);
			double D = 0;
			Neuron Winner = outputLayer[0, 0];
			double min = CalculateNormOfVectors(pattern, outputLayer[0, 0].Weights);
			for (int i = 0; i < outputLayerDimension; i++)
				for (int j = 0; j < outputLayerDimension; j++) {
					D = CalculateNormOfVectors(pattern, outputLayer[i, j].Weights);
					if (D < min) {
						min = D;
						Winner = outputLayer[i, j];
					}
				}
			return Winner;
		}

		public Dictionary<string, List<Neuron>> CenterNeuron {
			get;
			set;
		}

		public string DecideWinner(List<double> pattern) {
			if (Normalize) 
				this.NormalizeInputPattern(pattern);

			string[,] clusterMatrix = new string[outputLayerDimension, outputLayerDimension];

			int numOfClasses = NumberOfClasses();

			for (int i = 0; i < outputLayerDimension; i++)
				for (int j = 0; j < outputLayerDimension; j++)
					clusterMatrix[i, j] = "Unknow";

			for (int i = 0; i < patterns.Count; i++) {
				Neuron n = FindWinner(patterns[i]);
				clusterMatrix[n.Coordinate.X, n.Coordinate.Y] = classes[i];
			}

			var clusters = new Dictionary<string, Neuron>(numOfClasses);
			foreach (var item in ExistentClasses) {
				double avgX = 0.0;
				double avgY = 0.0;
				int count = 0;

				for (int i = 0; i < outputLayerDimension; i++)
					for (int j = 0; j < outputLayerDimension; j++) {
						if (item.Key == clusterMatrix[i, j]) {
							avgX += i;
							avgY += j;
							count++;
						}
					}
				avgX /= count;
				avgY /= count;

				for (int i = 0; i < outputLayerDimension; i++)
					for (int j = 0; j < outputLayerDimension; j++) {
						var dMin = double.MaxValue;
						if (item.Key == clusterMatrix[i, j]) {
							var min = Math.Pow(i - avgX, 2) + Math.Pow(j - avgY, 2);
							if (min < dMin) {
								if (clusters.ContainsKey(item.Key)) {
									clusters[item.Key] = outputLayer[i, j];
								}
								else {
									clusters.Add(item.Key, outputLayer[i, j]);
								}
							}
						}
					}
			}

			double minD = double.MaxValue;
			string clus = "";
			foreach (var item in clusters) {
				var D = CalculateNormOfVectors(pattern, item.Value.Weights);
				if (D < minD) {
					minD = D;
					clus = item.Key;
				}
			}
			return clus;
		}

		/// <summary>
		/// Bắt đầu quá trình học
		/// maxIterationNumber: Số bước tối đa
		/// minEpsilon: Epsilon tối thiểu
		/// </summary>
		public void StartLearning() {
			int iterations = 0;

			//Lặp lại đến khi số vòng lặp tối đa hoặc currentEpsilon đủ nhỏ = epsilon 
			while (iterations <= maxIterationNumber && currentEpsilon > minEpsilon) {
				List<List<double>> patternsToLearn = new List<List<double>>(numberOfPatterns);
				foreach (List<double> pArray in patterns)//Copy sang  patternsToLearn để ko làm thay đổi patterns
					patternsToLearn.Add(pArray);

				Random randomPattern = new Random();
				List<double> pattern = new List<double>(inputLayerDimension);
				for (int i = 0; i < numberOfPatterns; i++) //Với mỗi Vector
                {
					pattern = patternsToLearn[randomPattern.Next(numberOfPatterns - i)];//Lấy random trong số mẫu

					StartEpoch(pattern);

					patternsToLearn.Remove(pattern);//Loại bỏ partten đã xử lý
				}
				iterations++;
				OnEndIterationEvent(new EventArgs());
			}
		}

		public void ReadDataFromFile(string inputDataFileName) {
			StreamReader sr = new StreamReader(inputDataFileName);

			//TO DO: Khởi tạo ma trận trọng số ngẫu nhiên 

			//Lấy số chiều Vector
			string line = sr.ReadLine();
			int k = 0;
			for (int i = 0; i < line.Length; i++) {
				if (line[i] == ' ') k++;
			}

			inputLayerDimension = k;
			int sigma0 = outputLayerDimension;

			outputLayer = new Neuron[outputLayerDimension, outputLayerDimension];
			Random r = new Random();
			for (int i = 0; i < outputLayerDimension; i++)
				for (int j = 0; j < outputLayerDimension; j++) {
					outputLayer[i, j] = new Neuron(i, j, sigma0);

					//Khởi tạo trọng số ngẫu nhiên giữa 0 và 1 (NextDouble)
					outputLayer[i, j].Weights = new List<double>(inputLayerDimension);
					for (k = 0; k < inputLayerDimension; k++) {
						outputLayer[i, j].Weights.Add(r.NextDouble());
					}
				}
			//End khởi tạo

			//Lấy số Vector đầu vào
			k = 0;
			while (line != null) {
				line = sr.ReadLine();
				k++;
			}
			patterns = new List<List<double>>(k);
			classes = new List<string>(k);

			numberOfPatterns = k;

			List<double> pattern;

			sr = new StreamReader(inputDataFileName);
			line = sr.ReadLine();

			//Cập nhật các vector đầu vào
			while (line != null) {
				int startPos = 0;
				int endPos = 0;
				int j = 0;
				pattern = new List<double>(inputLayerDimension);
				for (int ind = 0; ind < line.Length; ind++) {
					if (line[ind] == ' ' && j != inputLayerDimension) {
						endPos = ind;
						pattern.Add(Convert.ToDouble(line.Substring(startPos, endPos - startPos)));
						startPos = ind + 1;
						j++;
					}
					if (j > inputLayerDimension) throw new InvalidDataException("Wrong file format. Check input data file, and try again");
				}
				//Nếu chuẩn hóa thì thực hiện chuẩn hóa đầu vào
				if (normalize) this.NormalizeInputPattern(pattern);

				//Thêm các vector
				patterns.Add(pattern);

				startPos = line.LastIndexOf(' ');

				//Thêm các class
				classes.Add(line.Substring(startPos));

				//Đọc hàng tiếp theo
				line = sr.ReadLine();
			}
		}

		public event EndEpochEventHandler EndEpochEvent;
		public event EndIterationEventHandler EndIterationEvent;

		protected virtual void OnEndEpochEvent(EndEpochEventArgs e) {
			if (EndEpochEvent != null)
				EndEpochEvent(this, e);
		}

		/// <summary>
		/// Event sau mỗi vòng
		/// </summary>
		/// <param name="e"></param>
		protected virtual void OnEndIterationEvent(EventArgs e) {
			if (EndIterationEvent != null)
				EndIterationEvent(this, e);
		}
	}
}